package junior.db.dao.update;

import junior.db.dao.base.SqlValue;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.BiConsumer;
import java.util.function.Consumer;

/**
 * @author wubo
 * @date 2024/9/3 15:20
 */
public class UpdateSqlProvider {
    
    public static String insert(Object entity, InsertOption option, Consumer<Object> valueConsumer) {
        UpdateOptionHelper.setGenerateKeys(option, entity.getClass());
        return UpdateSqlHelper.insert(entity, option, (path, val) -> {
            valueConsumer.accept(val);
            return "?";
        });
    }
    
    public static String batchInsert(List entities, BatchInsertOption option, BiConsumer<Object, Integer> entityValueConsumer) {
        UpdateOptionHelper.setGenerateKeys(option, entities.get(0).getClass());
        option.init(entities.get(0).getClass(), entities.toArray());
        option.beforeExecuteNode();
        
        StringBuilder buff = new StringBuilder();
        buff.append("insert into ").append(option.getTable());
        buff.append("(");
        String[] insertFields = option.getInsertFields();
        int fieldIndex = 0;
        for (int i = 0; i < insertFields.length; i++) {
            if (fieldIndex++ > 0) {
                buff.append(", ");
            }
            buff.append(option.getColumn(insertFields[i]));
        }
        buff.append(")");
        buff.append(" values (");
        
        int entityIndex = 0;
        for (Object[] values : option.getInsertValues()) {
            int valueIndex = 0;
            for (int i = 0; i < values.length; i++) {
                if (entityIndex == 0 && valueIndex++ > 0) {
                    buff.append(", ");
                }
                Object value = values[i];
                if (value instanceof SqlValue) {
                    SqlValue sqlValue = (SqlValue) value;
                    for (int j = 0; j < sqlValue.getList().size(); j++) {
                        SqlValue.Part part = sqlValue.getList().get(j);
                        if (part.isArg()) {
                            Object val = part.getVal();
                            if (entityIndex == 0) {
                                buff.append("?");
                            }
                            entityValueConsumer.accept(val, entityIndex);
                        } else {
                            if (entityIndex == 0) {
                                buff.append(part.getVal());
                            }
                        }
                    }
                } else {
                    if (entityIndex == 0) {
                        buff.append("?");
                    }
                    entityValueConsumer.accept(value, entityIndex);
                }
            }
            if (entityIndex == 0) {
                buff.append(")");
            }
            entityIndex++;
        }
        return buff.toString();
    }
    
    public static String upsert(Object entity, UpsertOption option, Consumer<Object> valueConsumer) {
        UpdateOptionHelper.setGenerateKeys(option, entity.getClass());
        return UpdateSqlHelper.upsert(entity, option, (path, val) -> {
            valueConsumer.accept(val);
            return "?";
        });
    }
    
    public static String batchUpsert(List entities, BatchUpsertOption option, BiConsumer<Object, Integer> entityValueConsumer) {
        UpdateOptionHelper.setGenerateKeys(option, entities.get(0).getClass());
        option.init(entities.get(0).getClass(), entities.toArray());
        option.beforeExecuteNode();
        
        StringBuilder buff = new StringBuilder();
        buff.append("insert into ").append(option.getTable());
        buff.append("(");
        String[] insertFields = option.getInsertFields();
        int fieldIndex = 0;
        for (int i = 0; i < insertFields.length; i++) {
            if (fieldIndex++ > 0) {
                buff.append(", ");
            }
            buff.append(option.getColumn(insertFields[i]));
        }
        buff.append(")");
        buff.append(" values (");
        
        int entityIndex = 0;
        for (Object[] values : option.getInsertValues()) {
            int valueIndex = 0;
            for (int i = 0; i < values.length; i++) {
                if (entityIndex == 0 && valueIndex++ > 0) {
                    buff.append(", ");
                }
                Object value = values[i];
                if (value instanceof SqlValue) {
                    SqlValue sqlValue = (SqlValue) value;
                    for (int j = 0; j < sqlValue.getList().size(); j++) {
                        SqlValue.Part part = sqlValue.getList().get(j);
                        if (part.isArg()) {
                            Object val = part.getVal();
                            if (entityIndex == 0) {
                                buff.append("?");
                            }
                            entityValueConsumer.accept(val, entityIndex);
                        } else {
                            if (entityIndex == 0) {
                                buff.append(part.getVal());
                            }
                        }
                    }
                } else {
                    if (entityIndex == 0) {
                        buff.append("?");
                    }
                    entityValueConsumer.accept(value, entityIndex);
                }
            }
            if (entityIndex == 0) {
                buff.append(")");
            }
            if (option.getUpdateFields().length > 0) {
                if (entityIndex == 0) {
                    buff.append(" on duplicate key update ");
                }
                for (int i = 0; i < option.getUpdateFields().length; i++) {
                    if (entityIndex == 0 && i > 0) {
                        buff.append(", ");
                    }
                    if (entityIndex == 0) {
                        buff.append(option.getColumn(option.getUpdateFields()[i])).append(" = ");
                    }
                    Object value = option.getUpdateValues()[i];
                    if (value instanceof SqlValue) {
                        SqlValue sqlValue = (SqlValue) value;
                        for (int j = 0; j < sqlValue.getList().size(); j++) {
                            SqlValue.Part part = sqlValue.getList().get(j);
                            if (part.isArg()) {
                                Object val = part.getVal();
                                if (entityIndex == 0) {
                                    entityValueConsumer.accept(val, entityIndex);
                                }
                            } else {
                                if (entityIndex == 0) {
                                    buff.append(part.getVal());
                                }
                            }
                        }
                    } else {
                        if (entityIndex == 0) {
                            entityValueConsumer.accept(value, entityIndex);
                        }
                    }
                }
            }
            entityIndex++;
        }
        return buff.toString();
    }
    
    public static String update(Object entity, UpdateOption option, Consumer<Object> valueConsumer) {
        return UpdateSqlHelper.update(entity, option, (path, val) -> {
            valueConsumer.accept(val);
            return "?";
        });
    }
    
    public static String updateField(Class<?> entityClass, UpdateFieldOption option, Consumer<Object> valueConsumer) {
        option.setEntityClass(entityClass);
        return UpdateSqlHelper.updateField(option, "", (path, val) -> {
            valueConsumer.accept(val);
            return "?";
        });
    }
    
    public static Map<String, List<List>> batchUpdate(List entities, BatchUpdateOption option) {
        option.init(entities.get(0).getClass(), entities.toArray());
        UpdateOptionHelper.batchSetVersion(option, entities.get(0).getClass());
        option.beforeExecuteNode();
        Map<String, List<List>> result = new HashMap<>();
        for (int entityIndex = 0; entityIndex < entities.size(); entityIndex++) {
            StringBuilder buff = new StringBuilder();
            List entityValues = new ArrayList();
            buff.append("update ").append(option.getTable());
            buff.append(" set ");
            int updateIndex = 0;
            
            Object[] values = option.getUpdateValues().get(entityIndex);
            for (Object value : values) {
                if (updateIndex > 0) {
                    buff.append(", ");
                }
                String field = option.getUpdateFields()[updateIndex];
                buff.append(option.getColumn(field)).append(" = ");
                if (value instanceof SqlValue) {
                    SqlValue sqlValue = (SqlValue) value;
                    for (int j = 0; j < sqlValue.getList().size(); j++) {
                        SqlValue.Part part = sqlValue.getList().get(j);
                        if (part.isArg()) {
                            Object val = part.getVal();
                            buff.append("?");
                            entityValues.add(val);
                        } else {
                            buff.append(part.getVal());
                        }
                    }
                } else {
                    buff.append("?");
                    entityValues.add(value);
                }
                updateIndex++;
            }
            buff.append(UpdateSqlHelper.getUpdateFilters(option.getEntityFilters().get(entityIndex), "option.entityFilters[" + entityIndex + "]", (path, val) -> {
                entityValues.add(val);
                return "?";
            }));
            List<List> list = result.computeIfAbsent(buff.toString(), k -> new ArrayList<>());
            list.add(entityValues);
        }
        return result;
    }
    
    public static String delete(DeleteOption option, Class<?> entityClass, Consumer<Object> valueConsumer) {
        option.setEntityClass(entityClass);
        return UpdateSqlHelper.delete(option, "", (path, val) -> {
            valueConsumer.accept(val);
            return "?";
        });
    }
}
